package generic.net;

import java.io.IOException;
import java.io.InputStream;

/**
 * Creates an inputstream within a normal input stream, so that XMLReader
 * can have a gay old time with a its own stream, having the close state
 * passed down to it without having the actual connection die.
 *
 * @see SubOutputStream
 * @author Adam Scarr <scarr.adam@gmail.com>
 */
public class SubInputStream extends InputStream {
    private InputStream in;
    private int size;
    private int offset = 0;

    /**
     * Creates a new SubInputStream.
     *
     * The creation of a new stream will block until the 6 byte header has
     * arrived.
     * @param in    The InputStream to wrap.
     * @throws java.io.IOException  If there was an issue reading the header
     */
    public SubInputStream(InputStream in) {
        this.in = in;

        // If we cant read the header, create an empty substream.
        try {
            readHeader();
        } catch (IOException ex) {
            size = 0;
            offset = 0;
        }

    }

    /**
     * Reads the substream header.
     * @throws java.io.IOException If the parent stream closes, or the header is invalid.
     */
    private void readHeader() throws IOException {
        byte[] header = new byte[6];
        int read;

        read = in.read(header, 0, 6);


        // first 4 bytes are just to be sure we havent stuffed up ordering, or otherwise
        // stuffed up our connection, if we have throw an exception.
        if (!(header[0] == 's' && header[1] == 'u' && header[2] == 'b' && header[3] == 's') || read < 6 || header == null) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < header.length; i++) {
                sb.append("[").append(header[i]).append("]");
            }
            throw new IOException("Mangled Header" + sb.toString());
        }

        // Convert from 2 byte big endian short to an int.
        size = ((0xFF & (int)header[4]) << 8) + (0xFF & (int)header[5]);
    }

    /**
     * Reads a byte from the substream. Does not allow reading past the end
     * of the substream.
     * @return Returns -1 if all the bytes have already been consumed
     * (as per InputStream.read()).
     */
    public int read() {
        if (offset < size) {
            offset++;
            try {
                int c = in.read();
                //               System.out.print((char)c);
                return c;
            } catch (IOException ex) {
                return -1;
            }
        }

        return -1;
    }

    /**
     * @return The number of remaining bytes in the substream.
     */
    public int available() {
        return size - offset;
    }
}